At one point in recent history, VB.Net was one of the most popular development languages around. In fact, there are many legacy applications written in VB.Net and even still, VB.Net is still in the top 10 languages ( source: https://www.techworm.net/2018/02/popular-programming-languages-2018-according-tiobe-pypl.html ). I will show a simple console application using VB.Net to authenticate using MSAL.Net
Of course, the first step, as in everything Azure, starts with setting up an app registration to use for Authentication. For this demonstration, I created a single tenant app registration with the following Redirect URI configured ( urn:ietf:wg:oauth:2.0:oob ):
There is nothing else special about this app registration.
Now, for the code. To start with, you must install the Microsoft.Identity.Client from the Nuget package manager. I will present to you sample VB.Net code that performs authentication synchronously, then asynchronously.
Imports Microsoft.Identity.Client Module Module1 Private _accessToken As String = String.Empty Private Const client_id As String = "{client_id}" '<-- enter the client_id guid here Private Const tenant_id As String = "{tenant_id}" '<-- enter either your tenant id here Private authority As String = $"https://login.microsoftonline.com/{tenant_id}" Private scopes As New List(Of String) Sub Main() scopes.Add($"{client_id}/.default") Console.WriteLine("Starting Synchronous Sample...") SyncSample() Console.WriteLine($"{Environment.NewLine}End Synchronous Sample...{Environment.NewLine}Start Asynchronous Sample...") AsyncSample() Console.WriteLine($"{Environment.NewLine}End Asynchronous Sample.{Environment.NewLine}Press any key to close...") Console.ReadKey() End Sub #Region "Synchronous Code" Private Sub SyncSample() If Login() Then Console.WriteLine(_accessToken) Else Console.WriteLine("Not Authorized") End If End Sub Private Function Login() As Boolean Dim publicClientApp As IPublicClientApplication publicClientApp = PublicClientApplicationBuilder.Create(client_id).WithAuthority(authority).Build() Dim accounts As IEnumerable(Of IAccount) = publicClientApp.GetAccountsAsync().Result() Dim firstAccount As IAccount = accounts.FirstOrDefault() Dim authResult As AuthenticationResult Try authResult = publicClientApp.AcquireTokenSilent(scopes, firstAccount).ExecuteAsync().Result() Catch e As MsalUiRequiredException Try authResult = publicClientApp.AcquireTokenInteractive(scopes).ExecuteAsync().Result() Catch ex As Exception 'user cancelled Return False End Try Catch ex As Exception Console.WriteLine($"Auth Exception: {ex.Message}") Return False End Try _accessToken = authResult.AccessToken Return True End Function #End Region #Region "Asynchronous Code" Private Sub AsyncSample() Dim task As Task(Of Boolean) = LoginTask() If task.Result() Then Console.WriteLine(_accessToken) Else Console.WriteLine("Not Authorized") End If End Sub Private Async Function LoginTask() As Task(Of Boolean) Dim publicClientApp As IPublicClientApplication publicClientApp = PublicClientApplicationBuilder.Create(client_id).WithAuthority(authority).Build() Dim accounts As IEnumerable(Of IAccount) = Await publicClientApp.GetAccountsAsync() Dim firstAccount As IAccount = accounts.FirstOrDefault() Dim authResult As AuthenticationResult Dim tryInteractive As Boolean = False Try authResult = Await publicClientApp.AcquireTokenSilent(scopes, firstAccount).ExecuteAsync() _accessToken = authResult.AccessToken Catch e As MsalUiRequiredException tryInteractive = True End Try If tryInteractive Then Try authResult = Await publicClientApp.AcquireTokenInteractive(scopes).ExecuteAsync() _accessToken = authResult.AccessToken Catch ex As Exception Return False End Try End If Return _accessToken <> String.Empty End Function #End Region End Module
Although there are quite a few differences from our C# samples, we can still leverage MSAL in our VB.Net code as well with some minor changes.
UPDATE: 06/18/2020
To clear up some confusion about the redirect URI, please try selecting the default MSAL redirect from the portal that is created when you create the app registration and after selecting that by clicking the checkbox, click the save button:
Then, modify your code to add the .WithRedirectUri parameter like so:
publicClientApp = PublicClientApplicationBuilder.Create(client_id).WithAuthority(authority).WithRedirectUri(“msal28a00d08-ba05-4015-a269-9d0546e850b9://auth”).Build()
The redirect URI I had in this post, I obtained to see what the default was sent during authentication by running fiddler, in an attempt to make this more universal for everyone, which apparently isn’t the case 🙂
Hi. That doesn’t work for me. I’m getting an Error when trying to log in.
“AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application:”
I know one can configure the redirection_URI in Azure-Portal, but the way i understand it the redirection_URI must be the same in your application. But where in your code can I set the redirection URI so that it matches the one I’ve set in Azure-Portal?
Yes, the redirection uri is part of the authentication process so they must match exactly. Try setting the redirect uri in both the portal and the app to match.
Also do you have the Correlation ID, Request ID, and Timestamp from the error message?
The Azure team in my firm is telling me that the registration won’t accept “urn:ietf:wg:oauth:2.0:oob” as a valid URI. Any suggestions?
Are you doing a native app or a web app or just using my sample? Try setting it to http://localhost but you will need to modify the sample app too and add that as a redirect uri since that is part of the authentication process. Like this:
publicClientApp = PublicClientApplicationBuilder.Create(client_id).WithRedirectUri(“http://localhost”).WithAuthority(authority).Build()
I got “AADSTS500113: No reply address is registered for the application. ” back
The redirect url you use in the app code must also be present in the reply url list on the app registration in the portal. They must also match exactly. If this is the case, I would suggest opening a support ticket so we can review the details of the app registration and your code.
This is still possible although the App Registration portal has changed a bit. In the Authentication blade, click on ‘Add a platform’ button under Platform configuration. Then choose ‘Mobile and desktop application’ you should then be able to add the above urn: reply URL in that section. The portal does some enforcing rule so you can’t add that reply URL into the ‘Web’ type client.
Hi,
I am getting accounts as zero always from below line. What could be the reason for that?
Any help is appreciated.
The GetAccountAsync method is looking for accounts that are present in the token cache. If you have never authenticated, then it would be expected to be 0. https://docs.microsoft.com/en-us/dotnet/api/microsoft.identity.client.clientapplicationbase.getaccountasync?view=azure-dotnet
Hi, I am also facing the same issue with the reply url. i have only one reply url for my AAD App and i have made sure the local host url is matching the url in the AAD App. however i keep getting the same error:
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application:
Any help would be appreciated.
Go to the portal and your app registration, select the default MSAL redirect uri and click save ( format of that one is “msal{client_id}://auth” so in my tenant, it is “msal28a00d08-ba05-4015-a269-9d0546e850b9://auth”. Then, in your code, where the public client is being set, after the .WithAuthority(….) add a .WithRedirectUri(“your new msal redirect”) but before the .build. So, like this ( using my example ):
publicClientApp = PublicClientApplicationBuilder.Create(client_id).WithAuthority(authority).WithRedirectUri(“msal28a00d08-ba05-4015-a269-9d0546e850b9://auth”).Build()
The redirect uri I have in this post, I obtained by running a fiddler trace to see what the default would be so that it would be the same for everyone but, seems that isn’t working out after all 😉
Hi, thanks for the good tutorial!
Unfortunately it doesn’t work for me.
I’m using .net framework 4.6 in my windows forms application.
When i try to call AcquireTokenInteractive, nothing happens.
[spoiler title=”Call”] authResult = Await publicClientApp.AcquireTokenInteractive(scopes).ExecuteAsync() [/spoiler]
The Program returns to my start window.
I tried to use different options between “publicClientApp.AcquireTokenInteractive(scopes).executeAsync()”.
Like “publicClientApp.AcquireTokenInteractive(scopes).WithParentActivityOrWindow(Me).executeAsync()”
I’m thankful for any suggestions. Thanks!
If you make a form with a button ( called btnLogin ) and a text box called ( textbox1 ) then something like this will need to be done.
Imports Microsoft.Identity.Client
Public Class Form1
Private accessToken As String = String.Empty
Private idToken As String = String.Empty
Private client_id As String = “{client_id}”
Private client_secret As String = “{client_secret}”
Private tenant_id As String = “{tenant_id}”
Private redirect_uri As String = “http://localhost”
Private scopes() As String = New String() {“openid offline_access User.Read”}
Private isLoggedIn As Boolean = False
Private Sub btnSignIn_Click(sender As Object, e As EventArgs) Handles btnSignIn.Click
‘we need a task to get MSAL to log us in
Dim task = New Task(AddressOf Login)
task.Start()
task.Wait()
End Sub
Private Async Sub Login()
Try
Dim app As IPublicClientApplication = PublicClientApplicationBuilder.Create(client_id).WithRedirectUri(redirect_uri).WithTenantId(tenant_id).WithAuthority(AadAuthorityAudience.AzureAdMyOrg).Build()
Dim authResult As AuthenticationResult = Nothing
accessToken = String.Empty
authResult = Await app.AcquireTokenInteractive(scopes).ExecuteAsync()
If authResult.AccessToken <> String.Empty Then
accessToken = authResult.AccessToken
End If
Catch ex As Exception
MessageBox.Show(ex.InnerException.Message)
End Try
‘Since this thread runs under the ui thread, we need a delegate method to update the text box
TextBox1.BeginInvoke(New InvokeDelegate(AddressOf InvokeMethod))
Return
End Sub
Private Delegate Sub InvokeDelegate()
Private Sub InvokeMethod()
TextBox1.Text = accessToken
End Sub
End Class
Thats it! Thanks a lot!